/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.awt;

import java.awt.peer.LightweightPeer;
import java.awt.peer.ScrollPanePeer;
import java.awt.event.*;
import javax.accessibility.*;
import sun.awt.ScrollPaneWheelScroller;
import sun.awt.SunToolkit;

import java.beans.ConstructorProperties;
import java.beans.Transient;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

/**
 * A container class which implements automatic horizontal and/or
 * vertical scrolling for a single child component.  The display
 * policy for the scrollbars can be set to:
 * <OL>
 * <LI>as needed: scrollbars created and shown only when needed by scrollpane
 * <LI>always: scrollbars created and always shown by the scrollpane
 * <LI>never: scrollbars never created or shown by the scrollpane
 * </OL>
 * <P>
 * The state of the horizontal and vertical scrollbars is represented
 * by two <code>ScrollPaneAdjustable</code> objects (one for each
 * dimension) which implement the <code>Adjustable</code> interface.
 * The API provides methods to access those objects such that the
 * attributes on the Adjustable object (such as unitIncrement, value,
 * etc.) can be manipulated.
 * <P>
 * Certain adjustable properties (minimum, maximum, blockIncrement,
 * and visibleAmount) are set internally by the scrollpane in accordance
 * with the geometry of the scrollpane and its child and these should
 * not be set by programs using the scrollpane.
 * <P>
 * If the scrollbar display policy is defined as "never", then the
 * scrollpane can still be programmatically scrolled using the
 * setScrollPosition() method and the scrollpane will move and clip
 * the child's contents appropriately.  This policy is useful if the
 * program needs to create and manage its own adjustable controls.
 * <P>
 * The placement of the scrollbars is controlled by platform-specific
 * properties set by the user outside of the program.
 * <P>
 * The initial size of this container is set to 100x100, but can
 * be reset using setSize().
 * <P>
 * Scrolling with the wheel on a wheel-equipped mouse is enabled by default.
 * This can be disabled using <code>setWheelScrollingEnabled</code>.
 * Wheel scrolling can be customized by setting the block and
 * unit increment of the horizontal and vertical Adjustables.
 * For information on how mouse wheel events are dispatched, see
 * the class description for {@link MouseWheelEvent}.
 * <P>
 * Insets are used to define any space used by scrollbars and any
 * borders created by the scroll pane. getInsets() can be used
 * to get the current value for the insets.  If the value of
 * scrollbarsAlwaysVisible is false, then the value of the insets
 * will change dynamically depending on whether the scrollbars are
 * currently visible or not.
 *
 * @author Tom Ball
 * @author Amy Fowler
 * @author Tim Prinzing
 */
public class ScrollPane extends Container implements Accessible {


  /**
   * Initialize JNI field and method IDs
   */
  private static native void initIDs();

  static {
        /* ensure that the necessary native libraries are loaded */
    Toolkit.loadLibraries();
    if (!GraphicsEnvironment.isHeadless()) {
      initIDs();
    }
  }

  /**
   * Specifies that horizontal/vertical scrollbar should be shown
   * only when the size of the child exceeds the size of the scrollpane
   * in the horizontal/vertical dimension.
   */
  public static final int SCROLLBARS_AS_NEEDED = 0;

  /**
   * Specifies that horizontal/vertical scrollbars should always be
   * shown regardless of the respective sizes of the scrollpane and child.
   */
  public static final int SCROLLBARS_ALWAYS = 1;

  /**
   * Specifies that horizontal/vertical scrollbars should never be shown
   * regardless of the respective sizes of the scrollpane and child.
   */
  public static final int SCROLLBARS_NEVER = 2;

  /**
   * There are 3 ways in which a scroll bar can be displayed.
   * This integer will represent one of these 3 displays -
   * (SCROLLBARS_ALWAYS, SCROLLBARS_AS_NEEDED, SCROLLBARS_NEVER)
   *
   * @serial
   * @see #getScrollbarDisplayPolicy
   */
  private int scrollbarDisplayPolicy;

  /**
   * An adjustable vertical scrollbar.
   * It is important to note that you must <em>NOT</em> call 3
   * <code>Adjustable</code> methods, namely:
   * <code>setMinimum()</code>, <code>setMaximum()</code>,
   * <code>setVisibleAmount()</code>.
   *
   * @serial
   * @see #getVAdjustable
   */
  private ScrollPaneAdjustable vAdjustable;

  /**
   * An adjustable horizontal scrollbar.
   * It is important to note that you must <em>NOT</em> call 3
   * <code>Adjustable</code> methods, namely:
   * <code>setMinimum()</code>, <code>setMaximum()</code>,
   * <code>setVisibleAmount()</code>.
   *
   * @serial
   * @see #getHAdjustable
   */
  private ScrollPaneAdjustable hAdjustable;

  private static final String base = "scrollpane";
  private static int nameCounter = 0;

  private static final boolean defaultWheelScroll = true;

  /**
   * Indicates whether or not scrolling should take place when a
   * MouseWheelEvent is received.
   *
   * @serial
   * @since 1.4
   */
  private boolean wheelScrollingEnabled = defaultWheelScroll;

  /*
   * JDK 1.1 serialVersionUID
   */
  private static final long serialVersionUID = 7956609840827222915L;

  /**
   * Create a new scrollpane container with a scrollbar display
   * policy of "as needed".
   *
   * @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
   * @see java.awt.GraphicsEnvironment#isHeadless
   */
  public ScrollPane() throws HeadlessException {
    this(SCROLLBARS_AS_NEEDED);
  }

  /**
   * Create a new scrollpane container.
   *
   * @param scrollbarDisplayPolicy policy for when scrollbars should be shown
   * @throws IllegalArgumentException if the specified scrollbar display policy is invalid
   * @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
   * @see java.awt.GraphicsEnvironment#isHeadless
   */
  @ConstructorProperties({"scrollbarDisplayPolicy"})
  public ScrollPane(int scrollbarDisplayPolicy) throws HeadlessException {
    GraphicsEnvironment.checkHeadless();
    this.layoutMgr = null;
    this.width = 100;
    this.height = 100;
    switch (scrollbarDisplayPolicy) {
      case SCROLLBARS_NEVER:
      case SCROLLBARS_AS_NEEDED:
      case SCROLLBARS_ALWAYS:
        this.scrollbarDisplayPolicy = scrollbarDisplayPolicy;
        break;
      default:
        throw new IllegalArgumentException("illegal scrollbar display policy");
    }

    vAdjustable = new ScrollPaneAdjustable(this, new PeerFixer(this),
        Adjustable.VERTICAL);
    hAdjustable = new ScrollPaneAdjustable(this, new PeerFixer(this),
        Adjustable.HORIZONTAL);
    setWheelScrollingEnabled(defaultWheelScroll);
  }

  /**
   * Construct a name for this component.  Called by getName() when the
   * name is null.
   */
  String constructComponentName() {
    synchronized (ScrollPane.class) {
      return base + nameCounter++;
    }
  }

  // The scrollpane won't work with a windowless child... it assumes
  // it is moving a child window around so the windowless child is
  // wrapped with a window.
  private void addToPanel(Component comp, Object constraints, int index) {
    Panel child = new Panel();
    child.setLayout(new BorderLayout());
    child.add(comp);
    super.addImpl(child, constraints, index);
    validate();
  }

  /**
   * Adds the specified component to this scroll pane container.
   * If the scroll pane has an existing child component, that
   * component is removed and the new one is added.
   *
   * @param comp the component to be added
   * @param constraints not applicable
   * @param index position of child component (must be &lt;= 0)
   */
  protected final void addImpl(Component comp, Object constraints, int index) {
    synchronized (getTreeLock()) {
      if (getComponentCount() > 0) {
        remove(0);
      }
      if (index > 0) {
        throw new IllegalArgumentException("position greater than 0");
      }

      if (!SunToolkit.isLightweightOrUnknown(comp)) {
        super.addImpl(comp, constraints, index);
      } else {
        addToPanel(comp, constraints, index);
      }
    }
  }

  /**
   * Returns the display policy for the scrollbars.
   *
   * @return the display policy for the scrollbars
   */
  public int getScrollbarDisplayPolicy() {
    return scrollbarDisplayPolicy;
  }

  /**
   * Returns the current size of the scroll pane's view port.
   *
   * @return the size of the view port in pixels
   */
  public Dimension getViewportSize() {
    Insets i = getInsets();
    return new Dimension(width - i.right - i.left,
        height - i.top - i.bottom);
  }

  /**
   * Returns the height that would be occupied by a horizontal
   * scrollbar, which is independent of whether it is currently
   * displayed by the scroll pane or not.
   *
   * @return the height of a horizontal scrollbar in pixels
   */
  public int getHScrollbarHeight() {
    int h = 0;
    if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) {
      ScrollPanePeer peer = (ScrollPanePeer) this.peer;
      if (peer != null) {
        h = peer.getHScrollbarHeight();
      }
    }
    return h;
  }

  /**
   * Returns the width that would be occupied by a vertical
   * scrollbar, which is independent of whether it is currently
   * displayed by the scroll pane or not.
   *
   * @return the width of a vertical scrollbar in pixels
   */
  public int getVScrollbarWidth() {
    int w = 0;
    if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) {
      ScrollPanePeer peer = (ScrollPanePeer) this.peer;
      if (peer != null) {
        w = peer.getVScrollbarWidth();
      }
    }
    return w;
  }

  /**
   * Returns the <code>ScrollPaneAdjustable</code> object which
   * represents the state of the vertical scrollbar.
   * The declared return type of this method is
   * <code>Adjustable</code> to maintain backward compatibility.
   *
   * @see java.awt.ScrollPaneAdjustable
   */
  public Adjustable getVAdjustable() {
    return vAdjustable;
  }

  /**
   * Returns the <code>ScrollPaneAdjustable</code> object which
   * represents the state of the horizontal scrollbar.
   * The declared return type of this method is
   * <code>Adjustable</code> to maintain backward compatibility.
   *
   * @see java.awt.ScrollPaneAdjustable
   */
  public Adjustable getHAdjustable() {
    return hAdjustable;
  }

  /**
   * Scrolls to the specified position within the child component.
   * A call to this method is only valid if the scroll pane contains
   * a child.  Specifying a position outside of the legal scrolling bounds
   * of the child will scroll to the closest legal position.
   * Legal bounds are defined to be the rectangle:
   * x = 0, y = 0, width = (child width - view port width),
   * height = (child height - view port height).
   * This is a convenience method which interfaces with the Adjustable
   * objects which represent the state of the scrollbars.
   *
   * @param x the x position to scroll to
   * @param y the y position to scroll to
   * @throws NullPointerException if the scrollpane does not contain a child
   */
  public void setScrollPosition(int x, int y) {
    synchronized (getTreeLock()) {
      if (getComponentCount() == 0) {
        throw new NullPointerException("child is null");
      }
      hAdjustable.setValue(x);
      vAdjustable.setValue(y);
    }
  }

  /**
   * Scrolls to the specified position within the child component.
   * A call to this method is only valid if the scroll pane contains
   * a child and the specified position is within legal scrolling bounds
   * of the child.  Specifying a position outside of the legal scrolling
   * bounds of the child will scroll to the closest legal position.
   * Legal bounds are defined to be the rectangle:
   * x = 0, y = 0, width = (child width - view port width),
   * height = (child height - view port height).
   * This is a convenience method which interfaces with the Adjustable
   * objects which represent the state of the scrollbars.
   *
   * @param p the Point representing the position to scroll to
   * @throws NullPointerException if {@code p} is {@code null}
   */
  public void setScrollPosition(Point p) {
    setScrollPosition(p.x, p.y);
  }

  /**
   * Returns the current x,y position within the child which is displayed
   * at the 0,0 location of the scrolled panel's view port.
   * This is a convenience method which interfaces with the adjustable
   * objects which represent the state of the scrollbars.
   *
   * @return the coordinate position for the current scroll position
   * @throws NullPointerException if the scrollpane does not contain a child
   */
  @Transient
  public Point getScrollPosition() {
    synchronized (getTreeLock()) {
      if (getComponentCount() == 0) {
        throw new NullPointerException("child is null");
      }
      return new Point(hAdjustable.getValue(), vAdjustable.getValue());
    }
  }

  /**
   * Sets the layout manager for this container.  This method is
   * overridden to prevent the layout mgr from being set.
   *
   * @param mgr the specified layout manager
   */
  public final void setLayout(LayoutManager mgr) {
    throw new AWTError("ScrollPane controls layout");
  }

  /**
   * Lays out this container by resizing its child to its preferred size.
   * If the new preferred size of the child causes the current scroll
   * position to be invalid, the scroll position is set to the closest
   * valid position.
   *
   * @see Component#validate
   */
  public void doLayout() {
    layout();
  }

  /**
   * Determine the size to allocate the child component.
   * If the viewport area is bigger than the preferred size
   * of the child then the child is allocated enough
   * to fill the viewport, otherwise the child is given
   * it's preferred size.
   */
  Dimension calculateChildSize() {
    //
    // calculate the view size, accounting for border but not scrollbars
    // - don't use right/bottom insets since they vary depending
    //   on whether or not scrollbars were displayed on last resize
    //
    Dimension size = getSize();
    Insets insets = getInsets();
    int viewWidth = size.width - insets.left * 2;
    int viewHeight = size.height - insets.top * 2;

    //
    // determine whether or not horz or vert scrollbars will be displayed
    //
    boolean vbarOn;
    boolean hbarOn;
    Component child = getComponent(0);
    Dimension childSize = new Dimension(child.getPreferredSize());

    if (scrollbarDisplayPolicy == SCROLLBARS_AS_NEEDED) {
      vbarOn = childSize.height > viewHeight;
      hbarOn = childSize.width > viewWidth;
    } else if (scrollbarDisplayPolicy == SCROLLBARS_ALWAYS) {
      vbarOn = hbarOn = true;
    } else { // SCROLLBARS_NEVER
      vbarOn = hbarOn = false;
    }

    //
    // adjust predicted view size to account for scrollbars
    //
    int vbarWidth = getVScrollbarWidth();
    int hbarHeight = getHScrollbarHeight();
    if (vbarOn) {
      viewWidth -= vbarWidth;
    }
    if (hbarOn) {
      viewHeight -= hbarHeight;
    }

    //
    // if child is smaller than view, size it up
    //
    if (childSize.width < viewWidth) {
      childSize.width = viewWidth;
    }
    if (childSize.height < viewHeight) {
      childSize.height = viewHeight;
    }

    return childSize;
  }

  /**
   * @deprecated As of JDK version 1.1, replaced by <code>doLayout()</code>.
   */
  @Deprecated
  public void layout() {
    if (getComponentCount() == 0) {
      return;
    }
    Component c = getComponent(0);
    Point p = getScrollPosition();
    Dimension cs = calculateChildSize();
    Dimension vs = getViewportSize();

    c.reshape(-p.x, -p.y, cs.width, cs.height);
    ScrollPanePeer peer = (ScrollPanePeer) this.peer;
    if (peer != null) {
      peer.childResized(cs.width, cs.height);
    }

    // update adjustables... the viewport size may have changed
    // with the scrollbars coming or going so the viewport size
    // is updated before the adjustables.
    vs = getViewportSize();
    hAdjustable.setSpan(0, cs.width, vs.width);
    vAdjustable.setSpan(0, cs.height, vs.height);
  }

  /**
   * Prints the component in this scroll pane.
   *
   * @param g the specified Graphics window
   * @see Component#print
   * @see Component#printAll
   */
  public void printComponents(Graphics g) {
    if (getComponentCount() == 0) {
      return;
    }
    Component c = getComponent(0);
    Point p = c.getLocation();
    Dimension vs = getViewportSize();
    Insets i = getInsets();

    Graphics cg = g.create();
    try {
      cg.clipRect(i.left, i.top, vs.width, vs.height);
      cg.translate(p.x, p.y);
      c.printAll(cg);
    } finally {
      cg.dispose();
    }
  }

  /**
   * Creates the scroll pane's peer.
   */
  public void addNotify() {
    synchronized (getTreeLock()) {

      int vAdjustableValue = 0;
      int hAdjustableValue = 0;

      // Bug 4124460. Save the current adjustable values,
      // so they can be restored after addnotify. Set the
      // adjustables to 0, to prevent crashes for possible
      // negative values.
      if (getComponentCount() > 0) {
        vAdjustableValue = vAdjustable.getValue();
        hAdjustableValue = hAdjustable.getValue();
        vAdjustable.setValue(0);
        hAdjustable.setValue(0);
      }

      if (peer == null) {
        peer = getToolkit().createScrollPane(this);
      }
      super.addNotify();

      // Bug 4124460. Restore the adjustable values.
      if (getComponentCount() > 0) {
        vAdjustable.setValue(vAdjustableValue);
        hAdjustable.setValue(hAdjustableValue);
      }
    }
  }

  /**
   * Returns a string representing the state of this
   * <code>ScrollPane</code>. This
   * method is intended to be used only for debugging purposes, and the
   * content and format of the returned string may vary between
   * implementations. The returned string may be empty but may not be
   * <code>null</code>.
   *
   * @return the parameter string of this scroll pane
   */
  public String paramString() {
    String sdpStr;
    switch (scrollbarDisplayPolicy) {
      case SCROLLBARS_AS_NEEDED:
        sdpStr = "as-needed";
        break;
      case SCROLLBARS_ALWAYS:
        sdpStr = "always";
        break;
      case SCROLLBARS_NEVER:
        sdpStr = "never";
        break;
      default:
        sdpStr = "invalid display policy";
    }
    Point p = (getComponentCount() > 0) ? getScrollPosition() : new Point(0, 0);
    Insets i = getInsets();
    return super.paramString() + ",ScrollPosition=(" + p.x + "," + p.y + ")" +
        ",Insets=(" + i.top + "," + i.left + "," + i.bottom + "," + i.right + ")" +
        ",ScrollbarDisplayPolicy=" + sdpStr +
        ",wheelScrollingEnabled=" + isWheelScrollingEnabled();
  }

  void autoProcessMouseWheel(MouseWheelEvent e) {
    processMouseWheelEvent(e);
  }

  /**
   * Process mouse wheel events that are delivered to this
   * <code>ScrollPane</code> by scrolling an appropriate amount.
   * <p>Note that if the event parameter is <code>null</code>
   * the behavior is unspecified and may result in an
   * exception.
   *
   * @param e the mouse wheel event
   * @since 1.4
   */
  protected void processMouseWheelEvent(MouseWheelEvent e) {
    if (isWheelScrollingEnabled()) {
      ScrollPaneWheelScroller.handleWheelScrolling(this, e);
      e.consume();
    }
    super.processMouseWheelEvent(e);
  }

  /**
   * If wheel scrolling is enabled, we return true for MouseWheelEvents
   *
   * @since 1.4
   */
  protected boolean eventTypeEnabled(int type) {
    if (type == MouseEvent.MOUSE_WHEEL && isWheelScrollingEnabled()) {
      return true;
    } else {
      return super.eventTypeEnabled(type);
    }
  }

  /**
   * Enables/disables scrolling in response to movement of the mouse wheel.
   * Wheel scrolling is enabled by default.
   *
   * @param handleWheel <code>true</code> if scrolling should be done automatically for a
   * MouseWheelEvent, <code>false</code> otherwise.
   * @see #isWheelScrollingEnabled
   * @see java.awt.event.MouseWheelEvent
   * @see java.awt.event.MouseWheelListener
   * @since 1.4
   */
  public void setWheelScrollingEnabled(boolean handleWheel) {
    wheelScrollingEnabled = handleWheel;
  }

  /**
   * Indicates whether or not scrolling will take place in response to
   * the mouse wheel.  Wheel scrolling is enabled by default.
   *
   * @see #setWheelScrollingEnabled(boolean)
   * @since 1.4
   */
  public boolean isWheelScrollingEnabled() {
    return wheelScrollingEnabled;
  }


  /**
   * Writes default serializable fields to stream.
   */
  private void writeObject(ObjectOutputStream s) throws IOException {
    // 4352819: We only need this degenerate writeObject to make
    // it safe for future versions of this class to write optional
    // data to the stream.
    s.defaultWriteObject();
  }

  /**
   * Reads default serializable fields to stream.
   *
   * @throws HeadlessException if <code>GraphicsEnvironment.isHeadless()</code> returns
   * <code>true</code>
   * @see java.awt.GraphicsEnvironment#isHeadless
   */
  private void readObject(ObjectInputStream s)
      throws ClassNotFoundException, IOException, HeadlessException {
    GraphicsEnvironment.checkHeadless();
    // 4352819: Gotcha!  Cannot use s.defaultReadObject here and
    // then continue with reading optional data.  Use GetField instead.
    ObjectInputStream.GetField f = s.readFields();

    // Old fields
    scrollbarDisplayPolicy = f.get("scrollbarDisplayPolicy",
        SCROLLBARS_AS_NEEDED);
    hAdjustable = (ScrollPaneAdjustable) f.get("hAdjustable", null);
    vAdjustable = (ScrollPaneAdjustable) f.get("vAdjustable", null);

    // Since 1.4
    wheelScrollingEnabled = f.get("wheelScrollingEnabled",
        defaultWheelScroll);

//      // Note to future maintainers
//      if (f.defaulted("wheelScrollingEnabled")) {
//          // We are reading pre-1.4 stream that doesn't have
//          // optional data, not even the TC_ENDBLOCKDATA marker.
//          // Reading anything after this point is unsafe as we will
//          // read unrelated objects further down the stream (4352819).
//      }
//      else {
//          // Reading data from 1.4 or later, it's ok to try to read
//          // optional data as OptionalDataException with eof == true
//          // will be correctly reported
//      }
  }

  class PeerFixer implements AdjustmentListener, java.io.Serializable {

    private static final long serialVersionUID = 1043664721353696630L;

    PeerFixer(ScrollPane scroller) {
      this.scroller = scroller;
    }

    /**
     * Invoked when the value of the adjustable has changed.
     */
    public void adjustmentValueChanged(AdjustmentEvent e) {
      Adjustable adj = e.getAdjustable();
      int value = e.getValue();
      ScrollPanePeer peer = (ScrollPanePeer) scroller.peer;
      if (peer != null) {
        peer.setValue(adj, value);
      }

      Component c = scroller.getComponent(0);
      switch (adj.getOrientation()) {
        case Adjustable.VERTICAL:
          c.move(c.getLocation().x, -(value));
          break;
        case Adjustable.HORIZONTAL:
          c.move(-(value), c.getLocation().y);
          break;
        default:
          throw new IllegalArgumentException("Illegal adjustable orientation");
      }
    }

    private ScrollPane scroller;
  }

/////////////////
// Accessibility support
////////////////

  /**
   * Gets the AccessibleContext associated with this ScrollPane.
   * For scroll panes, the AccessibleContext takes the form of an
   * AccessibleAWTScrollPane.
   * A new AccessibleAWTScrollPane instance is created if necessary.
   *
   * @return an AccessibleAWTScrollPane that serves as the AccessibleContext of this ScrollPane
   * @since 1.3
   */
  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null) {
      accessibleContext = new AccessibleAWTScrollPane();
    }
    return accessibleContext;
  }

  /**
   * This class implements accessibility support for the
   * <code>ScrollPane</code> class.  It provides an implementation of the
   * Java Accessibility API appropriate to scroll pane user-interface
   * elements.
   *
   * @since 1.3
   */
  protected class AccessibleAWTScrollPane extends AccessibleAWTContainer {

    /*
     * JDK 1.3 serialVersionUID
     */
    private static final long serialVersionUID = 6100703663886637L;

    /**
     * Get the role of this object.
     *
     * @return an instance of AccessibleRole describing the role of the object
     * @see AccessibleRole
     */
    public AccessibleRole getAccessibleRole() {
      return AccessibleRole.SCROLL_PANE;
    }

  } // class AccessibleAWTScrollPane

}

/*
 * In JDK 1.1.1, the pkg private class java.awt.PeerFixer was moved to
 * become an inner class of ScrollPane, which broke serialization
 * for ScrollPane objects using JDK 1.1.
 * Instead of moving it back out here, which would break all JDK 1.1.x
 * releases, we keep PeerFixer in both places. Because of the scoping rules,
 * the PeerFixer that is used in ScrollPane will be the one that is the
 * inner class. This pkg private PeerFixer class below will only be used
 * if the Java 2 platform is used to deserialize ScrollPane objects that were serialized
 * using JDK1.1
 */
class PeerFixer implements AdjustmentListener, java.io.Serializable {

  /*
   * serialVersionUID
   */
  private static final long serialVersionUID = 7051237413532574756L;

  PeerFixer(ScrollPane scroller) {
    this.scroller = scroller;
  }

  /**
   * Invoked when the value of the adjustable has changed.
   */
  public void adjustmentValueChanged(AdjustmentEvent e) {
    Adjustable adj = e.getAdjustable();
    int value = e.getValue();
    ScrollPanePeer peer = (ScrollPanePeer) scroller.peer;
    if (peer != null) {
      peer.setValue(adj, value);
    }

    Component c = scroller.getComponent(0);
    switch (adj.getOrientation()) {
      case Adjustable.VERTICAL:
        c.move(c.getLocation().x, -(value));
        break;
      case Adjustable.HORIZONTAL:
        c.move(-(value), c.getLocation().y);
        break;
      default:
        throw new IllegalArgumentException("Illegal adjustable orientation");
    }
  }

  private ScrollPane scroller;
}
